import 'dart:convert';
import 'dart:io';
import 'dart:async';
import 'package:dio/dio.dart';
import 'package:dio_http_cache/dio_http_cache.dart';
import 'package:flutter/material.dart';
import 'package:flutter_network/flutter_network.dart';
import 'package:flutter_network/src/network/http_base_params.dart';
import '../config/config.dart';
import 'http_interceptors.dart';
import 'http_response.dart';

/// 全局请求
// ignore: non_constant_identifier_names
HttpManager Http = HttpManager();

/// 请求管理，直接使用[Http]
class HttpManager {
  Dio _dio = Dio();

  Dio get dio => _dio;

  DioCacheManager? cacheManager;

  HttpManager() {
    // _init();
  }

  int requestSuccessCode = 1;
  FailHandle? requestFailHandle;
  ErrorHandle? requestErrorHandle;
  HttpContentType contentType = HttpContentType.form;

  // HttpContentType contentType = HttpContentType.json;

  /// !!!!初始化配置!!!!
  /// headerParams 请求头
  /// httpProxy 请求代理，不设置默认无
  /// successCode 自定义请求成功code，不设置默认为1
  /// cacheTime 自定义缓存时间，不设置默认7天，缓存只用在Get请求
  /// failHandle 接口报错回调，可用于处理业务code
  /// errorHandle http请求错误回调
  /// contentType [HttpContentType]
  void initConfig({
    Map<String, dynamic> Function()? headerParams,
    String? httpProxy,
    int? successCode,
    FailHandle? failHandle,
    ErrorHandle? errorHandle,
    HttpContentType? contentType,
    Duration? cacheTime,
    bool enableLog = true,
  }) {
    HttpInterceptors.httpInfoLog = enableLog;
    if (headerParams != null) {
      HttpBaseParams.setParams = headerParams();
    }
    requestSuccessCode = successCode ?? 1;
    requestFailHandle = failHandle;
    requestErrorHandle = errorHandle;
    this.contentType = contentType ?? HttpContentType.form;
    if (cacheTime != null) {
      Config.httpCacheTime = cacheTime;
    }
    if (httpProxy != null) {
      setupProxy(httpProxy);
    } else {
      _init();
    }
  }

  void _init({String? proxy}) {
    //连接服务器超时时间
    _setupOptions();
    //代理
    HttpInterceptors.dioProxy(_dio.httpClientAdapter, customProxy: proxy);
    //缓存
    _setupCache();
    //日志
    _setupLog();
  }

  _setupOptions() {
    _dio.options.connectTimeout = 60000;
    _dio.options.receiveTimeout = 0;
    _dio.options.sendTimeout = 60000;
    _dio.options.responseType = ResponseType.json;
  }

  _setupCache() {
    cacheManager = DioCacheManager(
      CacheConfig(
        //失效时间
        defaultMaxAge: Config.httpCacheTime,
        defaultRequestMethod: "GET",
        databaseName: "DioHttpCache",
        //最大的内存缓存数量
        maxMemoryCacheCount: 100,
      ),
    );
    dio.interceptors.add(cacheManager!.interceptor);
  }

  _setupLog() {
    if (Config.isPrintHttpLog == true) {
      if (Config.isRelease == true && Config.isPrintRelease == false) {
        return;
      }
      // HttpInterceptors.setupLog(_dio);
      dio.interceptors.add(
        LogInterceptor(
          responseBody: true,
          requestBody: true,
          logPrint: (obj) {
            debugPrint("$obj");
          },
        ),
      );
    }
  }

  ///设置自定义代理
  void setupProxy(String proxy) {
    _dio = Dio();
    _init(proxy: proxy);
  }

  /// get请求
  /// url 请求地址
  /// params 参数，map格式
  /// customHeader 自定义请求头，不传则按initConfig设置
  /// isToast response会返回该字段
  /// isCache 是否需要缓存改接口，默认是
  /// contentType [HttpContentType]
  Future<HttpResponseModel> get(
    String url, {
    Map<String, dynamic>? params,
    Map<String, dynamic>? customHeader,
    // Options options,
    bool isToast = true,
    bool isCache = true,
    HttpContentType? contentType,
  }) async {
    String requestUrl = url;
    var allParams = HttpBaseParams.params();
    allParams.addAll(params ?? {});
    Response response;
    HttpResponseModel result = HttpResponseModel();
    try {
      Map<String, dynamic> newQuery = Map<String, dynamic>.from(allParams);
      _dio.options.headers = await _requestHeader(customHeader, contentType: contentType);
      _setupOptions();
      response = await _dio.get(
        requestUrl,
        queryParameters: newQuery,
        options: isCache == true
            ? buildCacheOptions(
                Config.httpCacheTime,
                forceRefresh: true,
              )
            : null,
      );
      _handleResponse(result: result, response: response, isToast: isToast);
      return result;
    } on DioError catch (e) {
      _handleError(result: result, err: e, type: "get", isToast: isToast);
      return result;
    }
  }

  /// post请求
  /// url 请求地址
  /// bodyParams body参数，map格式
  /// urlParams query参数，map格式
  /// customHeader 自定义请求头，不传则按initConfig设置
  /// formData multipart/form-data 提交表单和文件, 默认false
  /// isToast response会返回该字段
  Future<HttpResponseModel> post(
    String url, {
    Map<String, dynamic>? bodyParams,
    Map<String, dynamic>? urlParams,
    Map<String, dynamic>? customHeader,
    bool formData = false,
    bool isToast = true,
    HttpContentType? contentType,
  }) async {
    String requestUrl = url;
    Map allParams = HttpBaseParams.params();
    allParams.addAll(urlParams ?? {});
    HttpResponseModel result = HttpResponseModel();
    Response response;
    try {
      var data;
      var queryParams = Map<String, dynamic>.from(allParams);
      if (formData == true) {
        data = FormData.fromMap(bodyParams ?? {});
      } else {
        if (bodyParams != null) {
          data = Map<String, dynamic>.from(bodyParams);
        }
      }
      _dio.options.headers = await _requestHeader(customHeader, contentType: contentType);
      _setupOptions();
      response = await _dio.post(
        requestUrl,
        data: data ?? {},
        queryParameters: queryParams,
      );
      _handleResponse(result: result, response: response, isToast: isToast);
      return result;
    } on DioError catch (e) {
      _handleError(result: result, err: e, type: "post", isToast: isToast);
      return result;
    }
  }

  /// put请求
  /// url 请求地址
  /// bodyParams body参数，map格式
  /// urlParams query参数，map格式
  /// customHeader 自定义请求头，不传则按initConfig设置
  Future<HttpResponseModel> put(
    String url, {
    Map<String, dynamic>? bodyParams,
    Map<String, dynamic>? urlParams,
    Map<String, dynamic>? customHeader,
    bool isToast = true,
    HttpContentType? contentType,
  }) async {
    String requestUrl = url;
    Map<String, dynamic> allParams = HttpBaseParams.params();
    allParams.addAll(urlParams ?? {});
    HttpResponseModel result = HttpResponseModel();
    Response response;
    try {
      var dataParams = Map<String, dynamic>.from(bodyParams ?? {});
      var queryParams = Map<String, dynamic>.from(allParams);
      _dio.options.headers = await _requestHeader(customHeader, contentType: contentType);
      _setupOptions();
      response = await _dio.put(
        requestUrl,
        data: dataParams,
        queryParameters: queryParams,
      );
      _handleResponse(result: result, response: response, isToast: isToast);
      return result;
    } on DioError catch (e) {
      _handleError(result: result, err: e, type: "post", isToast: isToast);
      return result;
    }
  }

  /// delete请求
  /// url 请求地址
  /// bodyParams body参数，map格式
  /// urlParams query参数，map格式
  /// customHeader 自定义请求头，不传则按initConfig设置
  Future<HttpResponseModel> delete(
    String url, {
    Map<String, dynamic>? bodyParams,
    Map<String, dynamic>? urlParams,
    Map<String, dynamic>? customHeader,
    bool isToast = true,
    HttpContentType? contentType,
  }) async {
    String requestUrl = url;
    Map allParams = HttpBaseParams.params();
    allParams.addAll(urlParams ?? {});
    HttpResponseModel result = HttpResponseModel();
    Response response;
    try {
      var dataParams = Map<String, dynamic>.from(bodyParams ?? {});
      var queryParams = Map<String, dynamic>.from(allParams);
      _dio.options.headers = await _requestHeader(customHeader, contentType: contentType);
      _setupOptions();
      response = await _dio.delete(
        requestUrl,
        data: dataParams,
        queryParameters: queryParams,
      );
      _handleResponse(result: result, response: response, isToast: isToast);
      return result;
    } on DioError catch (e) {
      _handleError(result: result, err: e, type: "post", isToast: isToast);
      return result;
    }
  }

  ///原生请求，不带任何默认参数
  Future<String?> rawGet(String url) async {
    var httpClient = HttpClient();
    String result;
    try {
      var request = await httpClient.getUrl(Uri.parse(url));
      var response = await request.close();
      if (response.statusCode == HttpStatus.ok) {
        var json = await response.transform(utf8.decoder).join();
        result = json;
        debugPrint('---------------------result $result');
        return result;
      } else {
        result = 'Error getting IP address:\nHttp status ${response.statusCode}';
        debugPrint('---------------------result $result');
        return null;
      }
    } catch (exception) {
      result = 'Failed getting $url';
      debugPrint('---------------------result $result');
      return null;
    }
  }

  Future<Map<String, dynamic>> _requestHeader(Map? customHeader,
      {HttpContentType? contentType}) async {
    Map<String, dynamic> newHeader;
    if (customHeader != null && customHeader.isNotEmpty) {
      newHeader = Map<String, dynamic>.from(customHeader);
    } else {
      newHeader = Map<String, dynamic>.from(HttpBaseParams.params());
    }
    if (!newHeader.containsKey("content-type")) {
      newHeader["content-type"] = (contentType ?? this.contentType).getTitle;
    }
    return newHeader;
  }

  void _handleResponse({
    required HttpResponseModel result,
    required Response response,
    bool isToast = true,
  }) {
    if (response.data != null && response.data is Map) {
      if (response.data["code"] == Http.requestSuccessCode) {
        result.isSuccess = true;
      } else {
        result.isFail = true;
      }
      result.data = response.data["data"];
      result.code = response.data["code"];
      result.message = response.data["msg"];
      result.statusCode = response.statusCode;
      result.hasMore = response.data["hasMore"] ?? false;
      result.headers = response.headers;
      //请求结果判断
      if (response.data.containsKey("code") == true) {
        int? code = response.data["code"];
        if (code == Http.requestSuccessCode) {
          //请求成功
        } else {
          //请求失败
          requestFailHandle?.call(
            failCode: result.code,
            httpCode: result.statusCode,
            message: result.message,
            isToast: isToast,
          );
          // switch (code) {
          //   default:
          //     {
          //       if (isToast == true) {
          //         _toast(msg: result.message, code: code);
          //       }
          //       break;
          //     }
          // }
        }
      }
    } else {
      result.isFail = true;
      result.message = response.statusMessage ?? "请求发生错误";
      result.statusCode = response.statusCode;
      result.response = response;
      requestFailHandle?.call(
        failCode: result.code,
        httpCode: result.statusCode,
        message: result.message,
        isToast: isToast,
      );
      // if (isToast == true) {
      //   _toast(msg: result.message, code: result.statusCode);
      // }
    }
  }

  void _handleError({
    required HttpResponseModel result,
    required DioError err,
    String? type,
    bool isToast = true,
  }) {
    result.isError = true;
    if (CancelToken.isCancel(err)) {
      debugPrint('$type 请求取消! ${err.message}');
    }
    result.message = err.message;
    result.statusCode = err.response?.statusCode ?? -1;
    result.response = null;
    try {
      debugPrint(
          '$type 请求错误||解析错误：error.msg:${err.message}, error.statusCode:${err.response?.statusCode}');
    } catch (e) {
      debugPrint('$type 请求错误||解析错误 err：$e');
    }
    requestErrorHandle?.call(
      httpCode: result.statusCode,
      message: err.message,
      isToast: isToast,
    );
    // if (err.response != null) {
    //   if (result.statusCode == 302) {
    //     debugPrint(' 请求异常信息 302: $err');
    //   } else {
    //     if (isToast == true) {
    //       _toast(msg: '网络异常，请稍候再试', code: result.statusCode);
    //     }
    //   }
    // } else {
    //   if (isToast == true) {
    //     _toast(msg: '网络异常，请稍候再试');
    //   }
    // }
  }
}

typedef FailHandle = void Function({
  int? failCode,
  int? httpCode,
  String? message,
  bool isToast,
});

typedef ErrorHandle = void Function({
  int? httpCode,
  String? message,
  bool isToast,
});

enum HttpContentType {
  json,
  form,
}

extension HttpContentTypeTitle on HttpContentType {
  String get getTitle {
    switch (this) {
      case HttpContentType.json:
        return "application/json; charset=utf-8";
      case HttpContentType.form:
        return "application/x-www-form-urlencoded";
    }
  }
}
